/**
 * 原型模式：用原型实例指定创建对象的种类，并且通过拷贝这些原型创建新的对象。通俗的讲就是当需要创建一个新的实例化对象时，我们刚好有一个实例化对象，但是已经存在的实例化对象又不能直接使用。这种情况下拷贝一个现有的实例化对象来用，可能会更方便。
 * 以下情形可以考虑使用原型模式：
 * 1.当new一个对象，非常繁琐复杂时，可以使用原型模式来进行复制一个对象。比如创建对象时，构造函数的参数很多，而自己又不完全的知道每个参数的意义，就可以使用原型模式来创建一个新的对象，不必去理会创建的过程。
 * 2.当需要new一个新的对象，这个对象和现有的对象区别不大，我们就可以直接复制一个已有的对象，然后稍加修改。
 * 3.当需要一个对象副本时，比如需要提供对象的数据，同时又需要避免外部对数据对象进行修改，那就拷贝一个对象副本供外部使用。
 */
#pragma once

#include <iostream>
#include <memory>

namespace prototype
{
//提供一个抽象克隆基类.
class Clone
{
public:
    virtual Clone* clone() = 0;
    virtual void show() = 0;
};

class Sheep : public Clone
{
public:
    Sheep(int id, const std::string& name) : Clone(), m_id(id), m_name(name) {}

    //关键代码拷贝构造函数
    Sheep(const Sheep& obj) {
        m_id = obj.m_id;
        m_name = obj.m_name;
    }

    Clone* clone() {
        return new Sheep(*this);
    }

    void show() {
        std::cout << "id:" << m_id << std::endl;
        std::cout << "name:" << m_name << std::endl;
    }

    void setId(int id) {
        m_id = id;
    }
    void setName(const std::string& name) {
        m_name = name;
    }
private:
    int         m_id;
    std::string m_name;
};

void ClientCode()
{
    unique_ptr<Sheep> s1(new Sheep(1, "name1"));
    s1->show();
    unique_ptr<Clone> s2(s1->clone());
    s2->show();
    cout << "changed s1:" << endl;
    s1->setId(2);
    s1->setName("name2");
    s1->show();
    s2->show();
}
} // namespace prototype
